package com.ms.common.util;

import com.ms.common.exception.BaseException;
import com.ms.common.function.IndexbleReturnableClosure;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

final public class CUtils {

    /**
     * null 或者 "" 谓词，用于控制在出现空值是 返回 true 还是返回 false
     */
    public static class NullOrEmptyPredicate implements Predicate {
        private boolean result;
        public NullOrEmptyPredicate(boolean result) {
            this.result = result;
        }
        @Override
        public boolean test(Object obj) {
            if (obj == null || (String.class.isAssignableFrom(obj.getClass()) && StringUtils.isBlank(obj.toString()))) {
                return result;
            }
            return !result;
        }
    }

    public static NullOrEmptyPredicate NullOrEmptyTrue = new NullOrEmptyPredicate(true);

    public static NullOrEmptyPredicate NullOrEmptyFalse = new NullOrEmptyPredicate(false);

    public static final List NULL_LIST = new NullList();

    /**
     * 表示长度是 0的 list
     */
    @SuppressWarnings("serial")
    public static class NullList extends AbstractList<Object> implements RandomAccess, Serializable {
        private NullList(){}
        @Override
        public int size() {return 0;}
        @Override
        public boolean contains(Object obj) {return false;}
        @Override
        public Object get(int index) {
            return null;
        }
        // Preserves singleton property
        private Object readResolve() {
            return NULL_LIST;
        }
    }

    /**
     * 如果集合 为null，创建长度为 0的集合； 它只是个表示，不能存在数据，不能遍历
     * @param col
     * @param <T>
     * @return
     */
    public static <T> Collection<T> emptyIfNull(Collection<T> col){
        if(col==null) {
            return Collections.emptyList();
        }
        return col;
    }

    /**
     * 如果集合 为null，创建长度为 0的集合； 它只是个表示，不能存在数据，不能遍历
     * @param col
     * @param <T>
     * @return
     */
    public static <T> List<T> emptyListIfNull(List<T> col){
        if(col==null) {
            return Collections.emptyList();
        }
        return col;
    }

    public static <K, V> Map<K, V> subtract(Map<K, V> first, Map<K, V> map){
        return subtract(first, map, false);
    }

    /**
     * 返回 存在第一个map中的元素，但去掉在第二个map中但元素
     * @param first
     * @param map
     * @param modifyFirst 是否可以修改第一个map 中的元素;
     *                    true:表示可以修改直接在map中移除在 第二map中存在的元素；
     *                    如果false:表示不能修改第一个map，需要创建新的
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> subtract(Map<K, V> first, Map<K, V> map, boolean modifyFirst){
        if(map==null || map.isEmpty()) {
            return first;
        }
        Map rs;
        if(modifyFirst) {
            rs = first;
        } else{
            rs = newMap(first.getClass());
            rs.putAll(first);
        }
        for(Map.Entry entry :map.entrySet()){
            rs.remove(entry.getKey());
        }
        return rs;
    }

    public static Map arrayIntoMap(Class mapClass, Object... params) {
        return arrayIntoMap(CUtils.newMap(mapClass), params);
    }

    /**
     * 根据 Map 的子类类型，创建新的map
     * @param clazz
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> newMap(Class<? extends Map> clazz){
        if(clazz==null) {
            //1、类型为null，直接创建 HashMap
            return new HashMap<>();
        }
        if(!Map.class.isAssignableFrom(clazz)) {
            throw new BaseException("class must be a map type: " + clazz);
        }
        //2、不是接口，直接反射创建
        if(!clazz.isInterface()){
            return ReflectUtils.newInstance(clazz);
        }

        if(ConcurrentMap.class.isAssignableFrom(clazz)){
            //3、是 并发map,创建 ConcurrentHashMap
            return new ConcurrentHashMap<>();
        }else if(SortedMap.class.isAssignableFrom(clazz)){
            //4、创建排序map
            return new TreeMap<>();
        }else{
            //5、默认创建 HashMap
            return new HashMap<>();
        }
    }

    /**
     * 对象数字 转成 map
     * @param params
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> asMap(Object... params) {
        //1、数字为空，创建空值 map,且不可以修改，即不能添加键值、不能遍历
        if(LangUtil.isEmpty(params)) {
            return Collections.emptyMap();
        }
        //2、
        Map<K, V> map = arrayIntoMap(CUtils.newHashMap(-1), params);
        return map;
    }

    public static <T> Map<T, T> typeMap(T... params) {
        if(LangUtil.isEmpty(params)) {
            return Collections.emptyMap();
        }
        Map<T, T> map = arrayIntoMap(CUtils.newHashMap(-1), params);
        return map;
    }


    public static Map<String, Object> fromProperties(Object obj) {
        return ReflectUtils.toMap(obj);
    }


    /**
     * 对象数组转换成 Map, 需要数组长度是 2的偶数，这样才能基于数据前后的值 构建 map的键值对，且对象数据
     * 每个下标元素不能为空； 偶数位作为key, 奇数位为 value
     * @param properties
     * @param params
     * @param <T>
     * @return
     */
    public static <T extends Map> T arrayIntoMap(T properties, Object... params) {
        if (params.length % 2 == 1) {
            throw new IllegalArgumentException("params is not key/value pair!");
        }
        int index = 0;
        Object name = null;
        for (Object s : params) {
            if (index % 2 == 0) {
                if (s == null || (String.class.isInstance(s) && StringUtils.isBlank(s.toString()))) {
                    throw new IllegalArgumentException("key can not be null or blank");
                }
                name = s;
            } else {
                properties.put(name, s);
            }
            index++;
        }

        return properties;
    }

    public static <K, V> LinkedHashMap<K, V> asLinkedMap(Object...params){
        return CUtils.arrayIntoMap(CUtils.newLinkedHashMap(), params);
    }

    public static <T> List<T> newArrayList(){
        return newArrayList(-1);
    }

    public static <T> List<T> newArrayList(int size){
        ArrayList<T> list = null;
        if(size<=0) {
            list = new ArrayList<T>();
        } else {
            list = new ArrayList<T>(size);
        }
        return list;
    }

    public static <T> List<T> newList(T...objs){
        ArrayList<T> list = new ArrayList<T>(Arrays.asList(objs));
        return list;
    }

    public static <T> T[] newArray(T...objs){
        return objs;
    }

    public static <T> HashSet<T> newHashSet(){
        return new HashSet<T>();
    }

    public static <T> HashSet<T> asSet(T...objs){
        HashSet<T> set = new HashSet<T>();
        for(T obj : objs){
            set.add(obj);
        }
        return set;
    }

    public static <T> HashSet<T> newHashSet(int size){
        HashSet<T> sets = null;
        if(size<=0) {
            sets = new HashSet<T>();
        } else {
            sets = new HashSet<T>(size);
        }
        return sets;
    }
    public static <T> TreeSet<T> newTreeSet(){
        return new TreeSet<T>();
    }

    public static <K, V> Map<K, V> newMap(){
        return newHashMap(-1);
    }

    public static <K, V> Map<K, V> newHashMap(int size){
        if(size<=0) {
            return new HashMap<>();
        }
        return new HashMap<>(size);
    }


    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int size){
        if(size<=0) {
            return new LinkedHashMap<>();
        }
        return new LinkedHashMap<>(size);
    }

    /**
     * 创建连表map
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(){
        return new LinkedHashMap<>();
    }

    public static <T> List<T> trimAsList(T... array) {
        return tolist(array, true);
    }
    public static <T> List<T> asList(T... array) {
        return tolist(array, false);
    }
    public static List tolist(Object object, boolean trimNull) {
        return tolist(object, trimNull, NULL_LIST);
    }

    public static <T> Collection<T> newCollections(Class<? extends Collection> clazz){
        return newCollections(clazz, null);
    }

    /**
     * @param clazz
     * @param size
     * @param <T>
     * @return
     */
    public static <T> Collection<T> newCollections(Class<? extends Collection> clazz, Integer size){
        if(clazz==null){
            return new ArrayList<T>();
        }
        if(!Collection.class.isAssignableFrom(clazz)) {
            throw new BaseException("class must be a Collection type: " + clazz);
        }

        if(!clazz.isInterface()){
            return ReflectUtils.newInstance(clazz);
        }

        if(List.class.isAssignableFrom(clazz)){
            return size==null?new ArrayList<T>():new ArrayList<T>(size);
        }else if(SortedSet.class.isAssignableFrom(clazz)){
            return new TreeSet<T>();
        }else if(Set.class.isAssignableFrom(clazz)){
            return size==null?new HashSet<T>():new HashSet<T>(size);
        }else if(Queue.class.isAssignableFrom(clazz)){
            return size==null?new ArrayDeque<T>():new ArrayDeque<T>(size);
        }else{
            return size==null?new ArrayList<T>():new ArrayList<T>(size);
        }
    }

    public static <T> Collection<T> toCollection(Object object) {
        if(object==null) {
            return Collections.emptyList();
        }
        List list = null;
        if (Collection.class.isAssignableFrom(object.getClass())) {
            return (Collection<T>)object;
        } else if (object.getClass().isArray()) {
            int length = Array.getLength(object);
            list = new ArrayList(length);
            appendToList(object, list);
        } else {
            list = new ArrayList(5);
            list.add(object);
        }
        return list;
    }

    /**
     * 把对象转换成 List，如果最后 list 为null,返回 默认 list
     * @param object
     * @param trimNull 是否去除空值
     * @param def
     * @return
     */
    public static List tolist(Object object, boolean trimNull, List def) {
        if (object == null) {
            return def;
        }
        List list = null;
        if (List.class.isInstance(object)) {
            //1、是List
            list = (List) object;
        } else if(Iterable.class.isInstance(object)){
            //2、是迭代器
            list = new ArrayList();
            for(Object obj : (Iterable<?>)object){
                list.add(obj);
            }
        }else if (object.getClass().isArray()) {
            //3、是数组
            int length = Array.getLength(object);
            list = new ArrayList(length);
            for (int i = 0; i < length; i++) {
                list.add(Array.get(object, i));
            }
        } else {
            //4、以上都不是，创建 list
            list = new ArrayList(5);
            list.add(object);
        }
        if (trimNull) {
            stripNull(list);
        }
        return (list == null || list.isEmpty()) ? def : list;
    }

    /**
     * 添加对象数组到集合
     * @param object
     * @param list
     */
    public static void appendToList(Object object, List list){
        //1、获取对象数组长度
        int length = Array.getLength(object);
        Object o;
        for(int i=0; i<length; i++){
            //2、循环获取对象数组中的每个元素
            o = Array.get(object, i);
            if(o==null) {
                //3、元素为空，继续循环
                continue;
            }
            if(o.getClass().isArray()) {
                //4、对象数组中元素还是数组，递归调用
                appendToList(o, list);
            } else {
                //5、元素添加到list
                list.add(o);
            }
        }
    }

    /**
     * 去掉集合中 空元素
     * @param collection
     * @return
     */
    public static Collection stripNull(Collection collection) {
        return strip(collection);
    }

    public static <T> List<T> trimAndexcludeTheClassElement(boolean trimNull, Object array, Object... excludeClasses) {//Class... excludeClasses
        if (array == null) {
            return NULL_LIST;
        }

        List list = null;
        if(array.getClass().isArray()){
            int length = Array.getLength(array);
            list = new ArrayList(length);
            for (int i = 0; i < length; i++) {
                list.add(Array.get(array, i));
            }
        }else{
            list = tolist(array, trimNull);
        }

        if (excludeClasses!=null && excludeClasses.length>0) {
            strip(list, excludeClasses);
        }
        return (list == null) ? NULL_LIST : list;
    }

    /****
     * remove specify value from list
     * default remove null or blank string
     * 删除传入的指定值
     * 默认删除空值
     * @param collection
     * @param stripValue
     * @return
     */
    public static Collection strip(Collection<?> collection, final Object... stripValue) {
        collection.removeIf(obj-> {
            if (obj instanceof Class) {
                if (ArrayUtils.isAssignableFrom(stripValue, (Class) obj)) {
                    return true;
                }
            } else if (obj == null || (String.class.isAssignableFrom(obj.getClass()) && StringUtils.isBlank(obj.toString())) || ArrayUtils.contains(stripValue, obj)){
               //
                return true;
            }
            return false;

        });
        return collection;
    }

    public static Object[] asArray(Object obj) {
        if(obj.getClass().isArray()){
            return (Object[]) obj;
        }else if(Collection.class.isAssignableFrom(obj.getClass())){
            Collection col = (Collection) obj;
            Object[] array = new Object[col.size()];
            int i = 0;
            for (Object o : col) {
                array[i++] = o;
            }
            return array;
        }else{
            return new Object[]{obj};
        }
    }

    public static String[] asStringArray(Object obj) {
        if(obj.getClass().isArray()){
            if(obj.getClass().getComponentType()==String.class){
                return (String[]) obj;
            }else{
                Object[] objArray = (Object[]) obj;
                String[] strs = new String[objArray.length];
                int index = 0;
                for(Object o : objArray){
                    strs[index++] = o.toString();
                }
                return strs;
            }
        }else if(Collection.class.isAssignableFrom(obj.getClass())){
            Collection col = (Collection) obj;
            String[] strs = new String[col.size()];
            int i = 0;
            for (Object o : col) {
                strs[i++] = o.toString();
            }
            return strs;
        }else if(Iterable.class.isAssignableFrom(obj.getClass())){
            List<String> list = new ArrayList<>();
            for (Object o : (Iterable) obj) {
                list.add(o.toString());
            }
            return list.toArray(new String[0]);
        }else{
            return new String[]{obj.toString()};
        }
    }

    public static Object[] map2Array(Map<?, ?> map){
        Object[] array = new Object[map.size()*2];
        int index = 0;
        for(Map.Entry<?, ?> entry : map.entrySet()){
            array[index++] = entry.getKey();
            array[index++] = entry.getValue();
        }
        return array;
    }


    /***
     *
     * @param collection
     * @param predicate 返回true，则保留；返回false，则移除
     */
    public static <T> void filter(Collection<T> collection, Predicate<T> predicate) {
        CollectionUtils.filter(collection, predicate);
    }

    /***
     *
     * @param map
     * @param predicate 返回true，则保留；返回false，则移除
     */
    public static <K, V> void filter(Map<K, V> map, Predicate<Map.Entry<K, V>> predicate) {
        if (map != null && predicate != null) {
            for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {
                if (predicate.test(it.next()) == false) {
                    it.remove();
                }
            }
        }
    }

    /*****
     * list1中存在，list2中找不到的元素
     * @param list1
     * @param list2
     * @param notInPredicate
     */
    public static <T> List<T> difference(List<T> list1, List<T> list2, NotInPredicate<T> notInPredicate) {
        List<T> diff = new ArrayList<>();
        for(T e : list1){
            if(notInPredicate.apply(e, list2)) {
                diff.add(e);
            }
        }
        return Collections.unmodifiableList(diff);
    }
    /****
     *
     * list1中存在，list2中找不到的元素
     * @param list1
     * @param list2
     * @param properties
     * @return
     */
    public static <T> List<T> difference(List<T> list1, List<T> list2, final String...properties) {
        return difference(list1, list2, new NotInPredicate<T>() {
            @Override
            public boolean apply(T e, List<T> list) {
                return !contains(list, e, properties);
            }
        });
    }

    public  interface NotInPredicate<T> {
        boolean apply(T e, List<T> list);
    }

    public  interface EqualsPredicate<T> {
        boolean apply(T e1, T e2);
    }

    public static <T> boolean containsAnyOne(Collection<T> c, T...elements){
        Assert.notEmpty(elements);
        if(LangUtil.isEmpty(c)) {
            return false;
        }
        for(T e : elements){
            if(c.contains(e)) {
                return true;
            }
        }
        return false;
    }

    public static <T> boolean contains(Collection<T> c, T element, EqualsPredicate<T> equalsPredicate){
        if(LangUtil.isNotEmpty(c)){
            for(T e : c){
                if(equalsPredicate.apply(e, element)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static <T> boolean contains(Collection<T> c, T element, final String...properties){
        return contains(c, element, new EqualsPredicate<T>(){
            @Override
            public boolean apply(T e1, T e2) {
                return isEquals(e1, e2, properties);
            }
        });
    }


    public static <T> boolean isEquals(T e1, T e2, EqualsPredicate<T> equalsPredicate){
        return equalsPredicate.apply(e1, e2);
    }

    /**
     *  比较两个对象中的 相同属性名称的值是否相等
     * @param e1
     * @param e2
     * @param properties
     * @param <T>
     * @return
     */
    public static <T> boolean isEquals(final T e1, final T e2, final String...properties){
        if(e1==e2) {
            return true;
        }
        if(e1==null || e2==null) {
            return false;
        }
        return isEquals(e1, e2, new EqualsPredicate<T>(){
            @Override
            public boolean apply(T e1, T e2) {
                for(String p : properties){
                    if(!ReflectUtils.getPropertyValue(e1, p).equals(ReflectUtils.getPropertyValue(e2, p))) {
                        return false;
                    }
                }
                return true;
            }
        });
    }



    public static Map<Integer, Object> toMap(List<?> list){
        return toMap(list, (e, index)->index);
    }

    public static <K> Map<K, Object> toMap(List<?> list, IndexbleReturnableClosure<Object, K> keyMap){
        Map<K, Object> map = new HashMap<>();
        int index = 0;
        for(Object e : list){
            K key = keyMap.execute(e, index);
            map.put(key, e);
            index++;
        }
        return map;
    }

    public static List<Object> toList(Map<?, ?> map) {
        if(map==null) {
            return NULL_LIST;
        }
        List<Object> list = new ArrayList(map.size()*2);
        for(Map.Entry<?, ?> entry : map.entrySet()){
            list.add(entry.getKey());
            list.add(entry.getValue());
        }
        return list;
    }

    public static <T, R> List<R> map(Collection<T> list, Function<? super T, ? extends R> mapper){
        Assert.notNull(list);
        Assert.notNull(mapper);
        return list.stream().map(mapper).collect(Collectors.toList());
    }

    public static <T> List<T> iterableToList(Iterable<T> it){
        if(it==null) {
            return Collections.EMPTY_LIST;
        }
        return StreamSupport.stream(it.spliterator(), false)
                .collect(Collectors.toList());
    }


    public static void removeByClass(List<?> list, Class<?> targetClass){
        Iterator<?> it = list.iterator();
        while(it.hasNext()){
            Object e = it.next();
            if(e!=null && targetClass.isAssignableFrom(ReflectUtils.getObjectClass(e))){
                it.remove();
            }
        }
    }


    /**
     * 查找 clazz 的所有父类，到这 stopClass停止
     * @param clazz
     * @param stopClass
     * @return
     */
    public static List<Class<?>> findSuperClasses(Class<?> clazz,Class<?> stopClass) {
        List<Class<?>> classes = new ArrayList<>();
        Class<?> parent = clazz.getSuperclass();
        while (parent != null && !parent.equals(stopClass)) {
            classes.add(parent);
            parent = parent.getSuperclass();
        }
        return classes;
    }

    /**
     * 获取 Class 内所有的 非静态、非瞬时台 字段Field
     * @param includeInherited
     * @return
     */
    public Collection<Field> getNotStaticAndTransientFields(Class<?> zclass,boolean includeInherited) {
        Collection<Field> fields = getFields(zclass,false);
        Collection<Field> rsfields = newArrayList();
        for (Field f : fields) {
            if(Modifier.isTransient(f.getModifiers()) || Modifier.isStatic(f.getModifiers())) {
                continue;
            }
            rsfields.add(f);
        }
        return rsfields;
    }

    public static List<Field> getFields(Class<?> clazz, boolean includeInherited) {
        List<Field> fields = new ArrayList<>();
        if (clazz == null) {
            return fields;
        }

        // 获取当前类中声明的所有字段
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            fields.add(field);
        }

        // 如果需要包含继承字段，则递归获取父类字段
        if (includeInherited) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !superClass.equals(Object.class)) {
                fields.addAll(getFields(superClass, true));
            }
        }
        return fields;
    }

    private CUtils(){
    }

}